using Viyi.Util.Extensions;
using Xunit;

namespace Viyi.Util.Test.Utils;

public class TestObjectExtensions {
    [Fact(DisplayName = "Also 处理事项并返回自身")]
    public void TestAlso() {
        // class
        string s = "hello";
        string rs = s.Also(ss => ss + "world");
        Assert.Same(s, rs);
        Assert.Null(((string?) null).Also(s => "hello" + s));

        // struct
        int i32 = 33;
        int r32 = i32.Also(n => n * 66);
        int x32 = ObjectExtensions.RefAlso(ref i32, n => n * 66);
        Assert.Equal(i32, r32);

        // nullable struct
        int? i32x = 33;
        int? r32x = i32.Also(n => n * 66);
        int? rr32x = test(i32);
        int? x = ObjectExtensions.RefAlso(ref i32x, n => (n ?? 0) * 66);
        Assert.Equal(i32x, r32x);

        int? test(int? v) {
            return v.RefAlso(n => (n ?? 0) * 33);
        }

        // property
        int len = s.Length.Also(length => length / 2);
    }

    [Fact(DisplayName = "Let 处理事项并返回新的结果")]
    public void TestLet() {
        // class
        string ss = "hello";
        string sr1 = ss.Let(s => s + "world");
        Assert.Equal("helloworld", sr1);
        string sr2 = ss.Let(s => { s += "world"; return s; });
        Assert.Equal("helloworld", sr1);
        string sr3 = ss.Let(s => { var sss = s + "world"; return s; });
        Assert.Same(ss, sr3);

        // struct
        int i32 = 32;
        int r32x = i32.Let(n => n * 2);
        Assert.Equal(32 * 2, r32x);

        // nullable
        Assert.Equal("null", ((int?) null).Let(testNullable));
        Assert.Equal("32", ((int?) 32).Let(n => testNullable(n)));
        int r32y = ((int?) 33).Let(n => (n ?? 0) * 2);
        Assert.Equal(66, r32y);
        Assert.Equal(20, ((int?) null).Let(n => (n ?? 10) * 2));

        string testNullable(int? n) {
            return n?.ToString() ?? "null";
        }
    }

    [Fact]
    public void TestLetWhen() {
        Assert.Equal("world", "hello".LetWhen(s => s == "hello", "world"));
        Assert.Equal("hello world", "".LetWhen(s => string.IsNullOrEmpty(s), () => "hello world"));
        Assert.NotEqual("world", "hello".LetWhenNot(s => s == "hello", "world"));
        Assert.NotEqual("hello world", "".LetWhenNot(s => string.IsNullOrEmpty(s), () => "hello world"));

        Assert.Equal(1, 1.LetWhen(n => n % 2 == 0, n => n * 2));
        Assert.Equal(4, 2.LetWhen(n => n % 2 == 0, n => n * 2));
    }

    [Fact]
    public async Task TestTakes() {
        Assert.Equal("hello", "hello".TakeIf(s => s == "hello"));
        Assert.Null("hello".TakeIf(s => s != "hello"));
        Assert.Equal("hello", "hello".TakeUnless(s => s != "hello"));
        Assert.Null("hello".TakeUnless(s => s == "hello"));

        Assert.Equal("hello", "hello".TakeIf(true));
        Assert.Null("hello".TakeIf(false));
    }

    [Fact]
    public async Task TestAsync() {
        var a = await "world".AlsoAsync(async it => await Task.Run(() => Console.WriteLine($"hello {it}")));
        var b = await "world".AlsoAsync(async it => Console.WriteLine($"hello {it}"));
    }
}
